package com.google.code.gaeapptest.junit;

import static org.junit.Assert.fail;

import java.io.BufferedReader;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.util.logging.Logger;

import org.junit.Test;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.files.AppEngineFile;
import com.google.appengine.api.files.FileReadChannel;
import com.google.appengine.api.files.FileService;
import com.google.appengine.api.files.FileServiceFactory;
import com.google.appengine.api.files.FileWriteChannel;

public class BlobTests extends BaseTest implements GaeTest {
	private static final Logger LOG = Logger.getLogger(BlobTests.class
			.getName());
	private static String TEST_TYPE = "Blob";
	private static String TEST_DATA = "Some test data";

	@Test
	public void all() throws Exception {
		long start = System.currentTimeMillis();
		createBlob();
		readBlob();
		updateBlob();
		deleteBlob();
		fetchBlob();
		long elapsed = System.currentTimeMillis() - start;
		saveResult(TEST_TYPE, elapsed, "OK");
	}

	@Test
	public void createBlob() throws Exception {
		long elapsed = -1L;
		String msg = "UNKNOWN";
		try {
			// Get a file service
			FileService fileService = FileServiceFactory.getFileService();
			long start = System.currentTimeMillis();
			// Create a new Blob file with mime-type "text/plain"
			AppEngineFile file = fileService.createNewBlobFile("text/plain");
			// Create doesnt create anything until you write to it
			FileWriteChannel writeChannel = fileService.openWriteChannel(file,
					true);
			writeChannel.write(ByteBuffer.wrap(TEST_DATA.getBytes()));
			writeChannel.closeFinally();
			elapsed = System.currentTimeMillis() - start;

			// read from the file using the file API
			FileReadChannel readChannel = fileService.openReadChannel(file,
					false);

			// Again, different standard Java ways of reading from the channel.
			BufferedReader reader = new BufferedReader(Channels.newReader(
					readChannel, "UTF8"));
			String line = reader.readLine();
			readChannel.close();
			if (line != null && line.startsWith(TEST_DATA)) {
				msg = "OK";
			} else {
				msg = "ERR line:" + line;
				elapsed = -1;
				fail("unable to create");
			}

			// Now delete the file using the Blobstore API
			BlobKey blobKey = fileService.getBlobKey(file);
			BlobstoreService blobStoreService = BlobstoreServiceFactory
					.getBlobstoreService();
			try {
				blobStoreService.delete(blobKey);
			} catch (Exception e) {
				LOG.warning("delete err: " + e.getMessage());
				e.printStackTrace();
			}
		} catch (Exception e) {
			msg = "Err:" + e.getMessage();
			fail("Err:" + e.getMessage());
			e.printStackTrace();
		} finally {
			saveResult(TEST_TYPE, elapsed, msg);
		}
	}

	@Test
	public void readBlob() throws Exception {
		long elapsed = -1L;
		String msg = "UNKNOWN";
		try {
			// Get a file service
			FileService fileService = FileServiceFactory.getFileService();

			// Create a new Blob file with mime-type "text/plain"
			AppEngineFile file = fileService.createNewBlobFile("text/plain");

			// Verify the create worked by writing to it
			FileWriteChannel writeChannel = fileService.openWriteChannel(file,
					true);
			writeChannel.write(ByteBuffer.wrap(TEST_DATA.getBytes()));
			writeChannel.closeFinally();

			// read from the file using the file API
			FileReadChannel readChannel = fileService.openReadChannel(file,
					false);

			long start = System.currentTimeMillis();
			// Again, different standard Java ways of reading from the channel.
			BufferedReader reader = new BufferedReader(Channels.newReader(
					readChannel, "UTF8"));
			String line = reader.readLine();
			readChannel.close();
			elapsed = System.currentTimeMillis() - start;
			if (line != null && line.startsWith(TEST_DATA)) {
				msg = "OK";
			} else {
				msg = "ERR line:" + line;
				elapsed = -1;
				fail("unable to read");
			}

			// Now delete the file using the Blobstore API
			BlobKey blobKey = fileService.getBlobKey(file);
			BlobstoreService blobStoreService = BlobstoreServiceFactory
					.getBlobstoreService();
			try {
				blobStoreService.delete(blobKey);
			} catch (Exception e) {
				LOG.warning("delete err: " + e.getMessage());
				e.printStackTrace();
			}
		} catch (Exception e) {
			msg = "Err:" + e.getMessage();
			fail("Err:" + e.getMessage());
			e.printStackTrace();
		} finally {
			saveResult(TEST_TYPE, elapsed, msg);

		}
	}

	@Test
	public void updateBlob() throws Exception {
		long elapsed = -1L;
		String msg = "UNKNOWN";
		try {
			// Get a file service
			FileService fileService = FileServiceFactory.getFileService();

			// Create a new Blob file with mime-type "text/plain"
			AppEngineFile file = fileService.createNewBlobFile("text/plain");

			// Open a channel to write to it
			boolean lock = true;
			FileWriteChannel writeChannel = fileService.openWriteChannel(file,
					lock);

			// Different standard Java ways of writing to the channel
			// are possible. Here we use a PrintWriter:
			PrintWriter out = new PrintWriter(Channels.newWriter(writeChannel,
					"UTF8"));
			out.println(TEST_DATA);

			// Close without finalizing and save the file path for writing later
			out.close();
			String path = file.getFullPath();

			// Write more to the file in a separate request:
			file = new AppEngineFile(path);

			// This time lock because we intend to finalize
			lock = true;
			long start = System.currentTimeMillis();
			writeChannel = fileService.openWriteChannel(file, lock);

			// This time we write to the channel using standard Java
			writeChannel.write(ByteBuffer.wrap("more data".getBytes()));

			// Now finalize
			writeChannel.closeFinally();
			elapsed = System.currentTimeMillis() - start;

			// Later, read from the file using the file API
			lock = false; // Let other people read at the same time
			FileReadChannel readChannel = fileService.openReadChannel(file,
					false);

			// Again, different standard Java ways of reading from the channel.
			BufferedReader reader = new BufferedReader(Channels.newReader(
					readChannel, "UTF8"));
			String line = reader.readLine();
			if (line != null && line.startsWith(TEST_DATA)) {
				msg = "OK";
			} else {
				msg = "ERR line:" + line;
				elapsed = -1;
				fail("unable to update");

			}
			readChannel.close();

			// Now read from the file using the Blobstore API
			BlobKey blobKey = fileService.getBlobKey(file);
			BlobstoreService blobStoreService = BlobstoreServiceFactory
					.getBlobstoreService();
			try {
				blobStoreService.delete(blobKey);
			} catch (Exception e) {
				LOG.warning("delete err: " + e.getMessage());
				e.printStackTrace();
			}
		} catch (Exception e) {
			msg = "Err:" + e.getMessage();
			fail("Err:" + e.getMessage());
			e.printStackTrace();
		} finally {
			saveResult(TEST_TYPE, elapsed, msg);

		}
	}

	@Test
	public void deleteBlob() throws Exception {
		long elapsed = -1L;
		String msg = "UNKNOWN";
		try {
			// Get a file service
			FileService fileService = FileServiceFactory.getFileService();

			// Create a new Blob file with mime-type "text/plain"
			AppEngineFile file = fileService.createNewBlobFile("text/plain");

			// Open a channel to write to it
			boolean lock = true;
			FileWriteChannel writeChannel = fileService.openWriteChannel(file,
					lock);

			PrintWriter out = new PrintWriter(Channels.newWriter(writeChannel,
					"UTF8"));
			out.println(TEST_DATA);
			writeChannel.closeFinally();

			// Now read from the file using the Blobstore API
			BlobKey blobKey = fileService.getBlobKey(file);
			BlobstoreService blobStoreService = BlobstoreServiceFactory
					.getBlobstoreService();
			long start = System.currentTimeMillis();
			try {
				blobStoreService.delete(blobKey);
			} catch (Exception e) {
				msg = "ERR delete:" + e.getMessage();
				LOG.warning("delete err: " + e.getMessage());
			}
			elapsed = System.currentTimeMillis() - start;
			blobKey = fileService.getBlobKey(file);
			if (blobKey == null) {
				msg = "OK";
			} else {
				msg = "ERR blobKey:" + blobKey;
				elapsed = -1;
				fail("unable to delete");
			}
		} catch (Exception e) {
			msg = "Err:" + e.getMessage();
			fail("Err:" + e.getMessage());
			e.printStackTrace();
		} finally {
			saveResult(TEST_TYPE, elapsed, msg);
		}
	}

	@Test
	public void fetchBlob() throws Exception {
		long elapsed = -1L;
		String msg = "UNKNOWN";
		try {
			// Get a file service
			FileService fileService = FileServiceFactory.getFileService();

			// Create a new Blob file with mime-type "text/plain"
			AppEngineFile file = fileService.createNewBlobFile("text/plain");

			// Verify the create worked by writing to it
			FileWriteChannel writeChannel = fileService.openWriteChannel(file,
					true);
			writeChannel.write(ByteBuffer.wrap(TEST_DATA.getBytes()));
			writeChannel.closeFinally();

			BlobKey blobKey = fileService.getBlobKey(file);
			BlobstoreService blobStoreService = BlobstoreServiceFactory
					.getBlobstoreService();
			long start = System.currentTimeMillis();
			byte[] data = blobStoreService.fetchData(blobKey, 0,
					TEST_DATA.length());
			elapsed = System.currentTimeMillis() - start;
			String line = new String(data);
			if (line != null && line.startsWith(TEST_DATA)) {
				msg = "OK";
			} else {
				msg = "ERR line:" + line;
				elapsed = -1;
				fail("unable to fetch");
			}
			try {
				// Now delete the file using the Blobstore API
				blobStoreService.delete(blobKey);
			} catch (Exception e) {
				LOG.warning("delete err: " + e.getMessage());
				e.printStackTrace();
			}
		} catch (Exception e) {
			msg = "Err:" + e.getMessage();
			fail("Err:" + e.getMessage());
			e.printStackTrace();
		} finally {
			saveResult(TEST_TYPE, elapsed, msg);
		}
	}
}
